﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using SSG.Tools;

namespace SSG.Model
{
    /// <summary>
    /// Navaid such as VOR, TACAN; NDB
    /// </summary>
    public class Navaid : Fix
    {
        /// <summary>
        /// Navaid types
        /// </summary>
        public enum NavaidType
        {
            Any, // only used for queries
            Dme,
            Ndb,
            Vor,
            VorDme,
            Ils,
            Ilsdme,
            Vortac
        }

        /// <summary>
        /// Navaids
        /// </summary>
        public static readonly List<Navaid> Navaids = new List<Navaid>();

        /// <summary>
        /// Type
        /// </summary>
        public NavaidType Type { get; set; }

        /// <summary>
        /// Frequency
        /// </summary>
        public double Frequency { get; set; }

        /// <summary>
        /// Frequency with unit
        /// </summary>
        public string FrequencyUnit
        {
            get
            {
                if (this.Frequency < 0) return "";
                return this.Type == NavaidType.Ndb ? this.Frequency.UnitKhz() : this.Frequency.UnitMhz();
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <param name="canoncialName"></param>
        /// <param name="description"></param>
        /// <param name="lat"></param>
        /// <param name="lng"></param>
        public Navaid(string name, NavaidType type, string canoncialName = null, string description = null, double lat = 0, double lng = 0) : base(name, canoncialName, description, lat, lng)
        {
            this.Type = type;
            this.Frequency = -1;
        }

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="otherNavaid"></param>
        public Navaid(Navaid otherNavaid) : this(otherNavaid.Name, otherNavaid.Type, otherNavaid.CanonicalName, otherNavaid.Description, otherNavaid.Latitude, otherNavaid.Longitude)
        {
            this.DataRawString = otherNavaid.DataRawString;
            this.IsCalculated = otherNavaid.IsCalculated;
            this.Frequency = otherNavaid.Frequency;
        }

        /// <summary>
        /// Read navaids
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static List<Navaid> ReadNavigraphPmdgNavaids(string file)
        {
            if (String.IsNullOrEmpty(file)) return null;
            if (!File.Exists(file)) return null;

            // create reader & read file
            string content = FileTools.ReadTextFile(file);
            if (String.IsNullOrEmpty(content)) return null;
            string[] lines = content.Split('\n');
            List<Navaid> navaids = new List<Navaid>();

            for (int line = 0; lines.Length > line; line++) {
                string cl = lines[line];
                if (String.IsNullOrWhiteSpace(cl)) continue;
                if (cl.StartsWith(";")) continue;
                Navaid navaid = Navaid.NavaidFromNavigraphPmdgFormat(cl);
                if (navaid == null) throw new InvalidOperationException("Line " + (line + 1) + " cannot be parsed: " + cl);
                navaids.Add(navaid);
            }

            Navaid.Navaids.Clear();
            Navaid.Navaids.AddRange(navaids);
            return Navaid.Navaids;
        }

        /// <summary>
        /// Object based on Navigraph PMDG format.
        /// </summary>
        /// <param name="rwLine"></param>
        /// <returns></returns>
        public static Navaid NavaidFromNavigraphPmdgFormat(string rwLine)
        {
            if (string.IsNullOrWhiteSpace(rwLine)) return null;
            rwLine = rwLine.Trim();

            // MARAMBIO                MBI  VORD-64.234722 -56.620000117.90H
            // ISLA REY JORGE          IRJ  NDB -62.199444 -58.961389360.00N
            //           1         2         3         4         5         6
            // 0123456789012345678901234567890123456789012345678901234567890

            string description = rwLine.Substring(0, 24).Trim();
            string designator = rwLine.Substring(24, 4).Trim();
            string typeStr = rwLine.Substring(29, 4).Trim();
            NavaidType type = Navaid.StringTypeToEnum(typeStr);
            double lat, lng, freq;
            if (!Double.TryParse(rwLine.Substring(33, 10).Trim(), NumberStyles.Float | NumberStyles.AllowThousands, Coordinates.ParseCulture, out lat)) return null;
            if (!Double.TryParse(rwLine.Substring(44, 10).Trim(), NumberStyles.Float | NumberStyles.AllowThousands, Coordinates.ParseCulture, out lng)) return null;
            if (!Double.TryParse(rwLine.Substring(54, 6).Trim(), NumberStyles.Float | NumberStyles.AllowThousands, Coordinates.ParseCulture, out freq)) return null;
            Navaid navaid = new Navaid(designator, type, designator + BaseEntity.CanonicalNameSeperator + type.ToString().ToUpperInvariant(), description, lat, lng) {Frequency = freq};
            return navaid;
        }

        /// <summary>
        /// Convert string to enum
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static NavaidType StringTypeToEnum(string type)
        {
            if ("VOR".Equals(type, StringComparison.InvariantCultureIgnoreCase)) return NavaidType.Vor;
            if ("VORD".Equals(type, StringComparison.InvariantCultureIgnoreCase)) return NavaidType.VorDme;
            if ("DME".Equals(type, StringComparison.InvariantCultureIgnoreCase)) return NavaidType.Dme;
            if ("ILS".Equals(type, StringComparison.InvariantCultureIgnoreCase)) return NavaidType.Ils;
            if ("ILSD".Equals(type, StringComparison.InvariantCultureIgnoreCase)) return NavaidType.Ilsdme;
            if ("NDB".Equals(type, StringComparison.InvariantCultureIgnoreCase)) return NavaidType.Ndb;
            if ("VORTAC".Equals(type, StringComparison.InvariantCultureIgnoreCase)) return NavaidType.Vortac;
            throw new ArgumentException("Unknown type " + type);
        }

        /// <summary>
        /// Convert type to an SVG image URL
        /// </summary>
        /// <param name="type"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public static string TypeToSvgUrl(NavaidType type, GeneratorSettings settings)
        {
            if (settings == null) throw new ArgumentNullException("settings");
            switch (type) {
                case NavaidType.Ndb:
                    return settings.SymbolNdbUrl;
                case NavaidType.VorDme:
                    return settings.SymbolVorDmeUrl;
                case NavaidType.Vor:
                    return settings.SymbolVorUrl;
                case NavaidType.Vortac:
                    return settings.SymbolVorTacUrl;
                default:
                    return settings.SymbolWaypointUrl;
            }
        }

        /// <summary>
        /// Get KML style id (name)
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string TypeToKmlStyleid(NavaidType type)
        {
            return typeof (Navaid).GetClassname() + "." + type.ToString();
        }

        /// <summary>
        /// Meaningful representation
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Type.ToString().ToUpperInvariant() + " " + base.ToString();
        }

        /// <summary>
        /// Kml representation
        /// </summary>
        /// <param name="level"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public override string ToKml(int level, GeneratorSettings settings)
        {
            string style = Navaid.TypeToKmlStyleid(this.Type);
            return Kml.Placemark(this.Name, this.Synopsis(), style, this.Latitude, this.Longitude, level, false, settings);
        }

        /// <summary>
        /// Synopsis
        /// </summary>
        /// <returns></returns>
        public new string Synopsis()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(this.Type.ToString().ToUpperInvariant());
            sb.AppendIfNotNullOrEmpty(this.Description, " ");
            sb.AppendIfNotNullOrEmpty(this.FrequencyUnit, " ");
            if (this.Altitude != null) {
                // in case of Navaids in a route we do have an alt
                sb.AppendIfNotNullOrEmpty(this.Altitude.Synopsis(), " ");
            }
            return sb.ToString();
        }

        /// <summary>
        /// Clone
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Navaid(this);
        }
    }
}